#include "wx/wxprec.h"
#include "wx/dir.h"
#include "wx/filename.h"

wxDirTraverseResult
wxDirTraverser::OnOpenError( const wxString& ( dirname ) ) {
  return wxDIR_IGNORE;
}

bool wxDir::HasFiles( const wxString& spec ) {
  wxString s;
  return GetFirst( &s, spec, wxDIR_FILES | wxDIR_HIDDEN );
}

#if (defined(__CYGWIN__) && defined(__WINDOWS__)) || !defined(__UNIX_LIKE__) || defined(__WXMAC__) || defined(__EMX__) || defined(__WINE__)

bool wxDir::HasSubDirs( const wxString& spec ) {
  wxString s;
  return GetFirst( &s, spec, wxDIR_DIRS | wxDIR_HIDDEN );
}

#endif

size_t wxDir::Traverse( wxDirTraverser& sink,
                        const wxString& filespec,
                        int flags ) const {
  wxCHECK_MSG( IsOpened(), ( size_t ) - 1, _T( "dir must be opened before traversing it" ) );
  size_t nFiles = 0;
  wxString prefix = GetName();
  prefix += wxFILE_SEP_PATH;
  if( flags & wxDIR_DIRS ) {
    wxString dirname;
    for( bool cont = GetFirst( &dirname, wxEmptyString, wxDIR_DIRS | ( flags & wxDIR_HIDDEN ) );
         cont;
         cont = cont && GetNext( &dirname ) ) {
      const wxString fulldirname = prefix + dirname;
      switch( sink.OnDir( fulldirname ) ) {
        default:
          wxFAIL_MSG( _T( "unexpected OnDir() return value" ) );
        case wxDIR_STOP:
          cont = false;
          break;
        case wxDIR_CONTINUE: {
          wxDir subdir;
          bool ok;
          do {
            wxLogNull noLog;
            ok = subdir.Open( fulldirname );
            if( !ok ) {
              bool tryagain;
              switch( sink.OnOpenError( fulldirname ) ) {
                default:
                  wxFAIL_MSG( _T( "unexpected OnOpenError() return value" ) );
                case wxDIR_STOP:
                  cont = false;
                case wxDIR_IGNORE:
                  tryagain = false;
                  break;
                case wxDIR_CONTINUE:
                  tryagain = true;
              }
              if( !tryagain )
              { break; }
            }
          } while( !ok );
          if( ok ) {
            nFiles += subdir.Traverse( sink, filespec, flags );
          }
        }
        break;
        case wxDIR_IGNORE:
          ;
      }
    }
  }
  if( flags & wxDIR_FILES ) {
    flags &= ~wxDIR_DIRS;
    wxString filename;
    bool cont = GetFirst( &filename, filespec, flags );
    while( cont ) {
      wxDirTraverseResult res = sink.OnFile( prefix + filename );
      if( res == wxDIR_STOP ) {
        break;
      }
      wxASSERT_MSG( res == wxDIR_CONTINUE,
                    _T( "unexpected OnFile() return value" ) );
      nFiles++;
      cont = GetNext( &filename );
    }
  }
  return nFiles;
}

class wxDirTraverserSimple : public wxDirTraverser {
  public:
    wxDirTraverserSimple( wxArrayString& files ) : m_files( files ) { }

    virtual wxDirTraverseResult OnFile( const wxString& filename ) {
      m_files.push_back( filename );
      return wxDIR_CONTINUE;
    }

    virtual wxDirTraverseResult OnDir( const wxString& ( dirname ) ) {
      return wxDIR_CONTINUE;
    }

  private:
    wxArrayString& m_files;

    DECLARE_NO_COPY_CLASS( wxDirTraverserSimple )
};

size_t wxDir::GetAllFiles( const wxString& dirname, wxArrayString *files, const wxString& filespec, int flags ) {
  wxCHECK_MSG( files, ( size_t ) - 1, _T( "NULL pointer in wxDir::GetAllFiles" ) );
  size_t nFiles = 0;
  wxDir dir( dirname );
  if( dir.IsOpened() ) {
    wxDirTraverserSimple traverser( *files );
    nFiles += dir.Traverse( traverser, filespec, flags );
  }
  return nFiles;
}

class wxDirTraverserFindFirst : public wxDirTraverser {
  public:
    wxDirTraverserFindFirst() { }

    virtual wxDirTraverseResult OnFile( const wxString& filename ) {
      m_file = filename;
      return wxDIR_STOP;
    }

    virtual wxDirTraverseResult OnDir( const wxString& ( dirname ) ) {
      return wxDIR_CONTINUE;
    }

    const wxString& GetFile() const {
      return m_file;
    }

  private:
    wxString m_file;

    DECLARE_NO_COPY_CLASS( wxDirTraverserFindFirst )
};

wxString wxDir::FindFirst( const wxString& dirname,
                           const wxString& filespec,
                           int flags ) {
  wxDir dir( dirname );
  if( dir.IsOpened() ) {
    wxDirTraverserFindFirst traverser;
    dir.Traverse( traverser, filespec, flags | wxDIR_FILES );
    return traverser.GetFile();
  }
  return wxEmptyString;
}

class wxDirTraverserSumSize : public wxDirTraverser {
  public:
    wxDirTraverserSumSize() { }

    virtual wxDirTraverseResult OnFile( const wxString& filename ) {
      wxULongLong sz = wxFileName::GetSize( filename );
      if( sz == wxInvalidSize ) {
        m_skippedFiles.Add( filename );
        return wxDIR_CONTINUE;
      }
      m_sz += sz;
      return wxDIR_CONTINUE;
    }

    virtual wxDirTraverseResult OnDir( const wxString& ( dirname ) ) {
      return wxDIR_CONTINUE;
    }

    wxULongLong GetTotalSize() const
    { return m_sz; }
    wxArrayString &FilesSkipped() { return m_skippedFiles; }

  protected:
    wxULongLong m_sz;
    wxArrayString m_skippedFiles;
};

wxULongLong wxDir::GetTotalSize( const wxString &dirname, wxArrayString *filesSkipped ) {
  if( !wxDirExists( dirname ) ) {
    return wxInvalidSize;
  }
  wxDir dir( dirname );
  if( !dir.IsOpened() ) {
    return wxInvalidSize;
  }
  wxDirTraverserSumSize traverser;
  if( dir.Traverse( traverser ) == ( size_t ) - 1 ||
      traverser.GetTotalSize() == 0 ) {
    return wxInvalidSize;
  }
  if( filesSkipped ) {
    *filesSkipped = traverser.FilesSkipped();
  }
  return traverser.GetTotalSize();
}

